/*$T indentinput.c GC 1.140 02/15/09 22:52:47 */

/*
 * Rs495.c ;
 * RS485 network protocol ;
 * * Version 1.0 beta ;
 * * Processor: PIC16F87x ;
 * * Network packet protocol: ;
 * * STX ( 0x02 ) ;
 * NET ADDRESS HIGH ;
 * NET ADDRESS LOW ;
 * PAYLOAD LENGTH ;
 * COMMAND ;
 * DATA ( Optional ) ;
 * CRC HIGH ;
 * CRC LOW ;
 * * By: J.Winpenny ;
 * j.winpenny@ukonline.co.uk ;
 * * Implementation using OSA By Simone Fabris ;
 * simone.fabris@gmail.com ;
 * * ;
 */
#include <stddef.h>
#include "Generic.h"
#include "OSA.h"
#include "service/osa_flag.h"
#include "delay.h"
#include "include/rs485.h"
#include "Config.h"
#include "crc.h"
#include "interrupt.h"

#if defined(__18CXX)
#include "alloc.h"
#endif

/* Global Variable */
OST_FLAG Rs485Flag;
OST_FLAG Rs485ErrorFlag;
OST_MSG drv_rs485_msg_rx;
OST_MSG drv_rs485_msg_tx;
OST_MSG drv_rs485_smsg_char;
OST_QUEUE DRV_rs485_qmsg_rx;
OST_QUEUE DRV_rs485_qmsg_tx;
OST_SQUEUE DRV_rs485_q_char;
OST_MSG DRV_rs485_msg_q_tx[RS485_SIZEOF_MSGQ];
OST_MSG DRV_rs485_msg_q_rx[RS485_SIZEOF_MSGQ];
OST_SMSG DRV_rs485_smsg_q_char[RS485_SIZEOF_CBUF];
unsigned char *strPacket;
unsigned char *p;
#if defined(__DEBUG)
RxPacket *rxPacket;
CommProt *commProt;
#else
static RxPacket *rxPacket;
static CommProt *commProt;
#endif

static void Rs485Process(RxPacket *, CommProt *);
static rxState Rs485Decode(RxPacket *, CommProt *, char);

/*
 * void Drv_Rs485(void) ;
 * * task for rs485 ;
 */
#if defined(__C30__)

/**
 =======================================================================================================================
 * Driver OSA per RS 485 - C30
 =======================================================================================================================
 */
void Drv_Rs485(void) {
    /*~~~~~~~~~~~~~~~~~~~~~~*/
    static int szof;
    static TxPacket *txPack;
    extern RxPacket *rxPacket;
    extern CommProt *commProt;
    static unsigned char rxChar;
    /*~~~~~~~~~~~~~~~~~~~~~~*/

    while (1) {
        OS_Flag_Wait_1x(Rs485Flag, 0xff);
        Nop();
        if OS_Flag_Check_1x(Rs485Flag, SER_RX) {
            while (OS_SQueue_Check(DRV_rs485_q_char)) {
                OS_SQueue_Wait(DRV_rs485_q_char, rxChar);
                Rs485Decode(rxPacket, commProt, rxChar);
                Rs485Process(rxPacket, commProt);
            }
        }

        if OS_Flag_Check_1x(Rs485Flag, SER_TX) {
            while (OS_Queue_Check(DRV_rs485_qmsg_tx)) {
                OS_Queue_Wait(DRV_rs485_qmsg_tx, drv_rs485_msg_tx);
                txPack = (TxPacket *) drv_rs485_msg_tx;

                /* Fixme più sicuro check su TxPacket, free e */
                szof = sizeof (TxPacket) + strlen(txPack->c485Buf);
                if (strPacket == 0x0) {
	                //FIXME non serive a niente
                    if (p > (char *) 0)
                        free(p);
                    strPacket = calloc(MAX_DATA_LENGTH, sizeof (char));

                    /* save the pointer */
                    p = strPacket;
                }

                TX_len = Rs485BuildPacket(strPacket, txPack);
                if (TX_len > 0) {
                    if (IEC0bits.U1TXIE == 0) {
                        U1STAbits.UTXEN = 1;
                        while (U1STAbits.UTXBF);
                        U1TXREG = strPacket[TX_ByteNumber++];
                        IEC0bits.U1TXIE = 1;
                    }
                }
            }
        }

        if OS_Flag_Check_1x(Rs485Flag, SER_ERR) {
            commProt->nrSerialError++;
            Nop();
        }

        Nop();
        OS_Flag_Set_0(Rs485Flag, 0xff);
    }
}

#elif defined(__18CXX)

/**
 =======================================================================================================================
 * Driver OSA per RS 485 - C18
 =======================================================================================================================
 */
void Drv_Rs485(void) {
    /*~~~~~~~~~~~~~~~~~~~~~~*/
    static int szof;
    static TxPacket *txPack;
    extern RxPacket *rxPacket;
    extern CommProt *commProt;
    static char rxChar;
    /*~~~~~~~~~~~~~~~~~~~~~~*/

    while (1) {
        OS_Flag_Wait_1x(Rs485Flag, 0xff);
        Nop();
        if OS_Flag_Check_1x(Rs485Flag, SER_RX) {

            /* Ricezione */
            while (OS_SQueue_Check(DRV_rs485_q_char)) {
                OS_SQueue_Wait(DRV_rs485_q_char, rxChar);
                Rs485Decode(rxPacket, commProt, rxChar);
                Rs485Process(rxPacket, commProt);
                /* Il pacchetto � valido e per noi */
                if (commProt->isPacketForMe && (commProt->cError == OK_CRC)) {
                }                    /* il pacchetto non era per noi o era invalido */
                else {

                }

            }
        }

        /* Trasmissione	*/
        if OS_Flag_Check_1x(Rs485Flag, SER_TX) {
            while (OS_Queue_Check(DRV_rs485_qmsg_tx)) {
                OS_Queue_Wait(DRV_rs485_qmsg_tx, drv_rs485_msg_tx);
                txPack = (TxPacket *) drv_rs485_msg_tx;

                /* Fixme più sicuro check su TxPacket, free e */
                szof = sizeof (TxPacket) + strlen(txPack->c485Buf);
                if (strPacket == 0x0) {
                    if (p > (unsigned char *) 0)
                        free(p);
                    strPacket = calloc(MAX_DATA_LENGTH, sizeof (char));

                    /* save the pointer */
                    p = strPacket;
                }

                TX_len = Rs485BuildPacket(strPacket, txPack);
                if (TX_len > 0) {
                    if ((PIE1bits.TXIE == 0) && (PIR1bits.TXIF == 0)) {
                        TXSTAbits.TXEN = 1;
                        PIE1bits.TXIE = 1;
                        TXREG = strPacket[TX_ByteNumber++];
                        TXSTAbits.TXEN = 1;
                        PIE1bits.TXIE = 1;
                    }
                }
            }
        }

        if OS_Flag_Check_1x(Rs485Flag, SER_ERR) {
            commProt->nrSerialError++;
            Nop();
        }

        Nop();
        OS_Flag_Set_0(Rs485Flag, 0xff);
    }
}
#endif

/**
 =======================================================================================================================
 * void Rs485Initialise(void) ;
 * * Initialise RS485 network driver ;
 *
 =======================================================================================================================
 */
void Rs485Initialise(WORD cAddr) {
    /*~~~~~~~~~~~~~~~~~~~~~~*/
    extern RxPacket *rxPacket;
    /*~~~~~~~~~~~~~~~~~~~~~~*/

    commProt = calloc(1, sizeof (CommProt));
    commProt->cOurAddr = cAddr;
    commProt->cRS485State = WAIT_START;
    commProt->isPacketForMe = FALSE;
    OS_Flag_Create(Rs485Flag);
    OS_Flag_Init(Rs485Flag, 0x0);
    TX_ByteNumber = 0;
    rxPacket = calloc(1, sizeof (RxPacket));
    rxPacket->c485Buf = calloc(MAX_DATA_LENGTH, sizeof (char));
}

/**
 =======================================================================================================================
 * static BOOL Rs485Process(RxPacket *rxPacket, CommProt *commProt) ;
 * * Decide if packet valid and destined for this node. ;
 * Ignore invalid packets and packets for other nodes ;
 *
 =======================================================================================================================
 */
static void Rs485Process(RxPacket *rxPacket, CommProt *commProt) {

    /* Pacchetto completo */
    if (commProt->cRS485State == COMPLETE) {

        /* Pacchetto valido */
        if (commProt->cOurAddr == rxPacket->cNetAddr)
            commProt->isPacketForMe = TRUE;
        if ((PostValidatePacket(rxPacket) == VALID)) {

            /* Pacchetto valido destinato a me, */
            if ((commProt->isPacketForMe)) {
                commProt->isPacketForMe = FALSE;
                commProt->cRS485State = WAIT_START;
                commProt->cBufPtr = 0;
                commProt->nrOkRxPacketForMe++;

                /* FIXME liberare memoria (buffer)... */
            }/* Pacchetto valido destinato ad altri */
            else {
                Nop();
                commProt->isPacketForMe = FALSE;
                commProt->cRS485State = WAIT_START;
                commProt->cBufPtr = 0;
                commProt->nrOkRxPacketForOther++;
            }
            /* FIXME liberare memoria (buffer)... */
        }/* Pacchetto completo non valido */
        else {
            commProt->nrWrongPacket++;
            commProt->isPacketForMe = FALSE;
            commProt->cRS485State = WAIT_START;
            commProt->cBufPtr = 0;
            commProt->nrOkRxPacketForOther++;
            Nop();


        }
    }
}

/**
 =======================================================================================================================
 * rxState Rs485Decode(RxPacket *rxPacket, CommProt *commProt, char rxChar)) ;
 * * Decode an incomming packet on the RS485 network ;
 * * Expecting: ;
 * START, ;
 * NETWORK ADDRESS_HIGH, ;
 * NETWORK ADDRESS_LOW, ;
 * PAYLOAD LENGTH, ;
 * COMMAND, ;
 * optional DATA, ;
 * CRC HIGH, ;
 * CRC LOW ;
 *
 =======================================================================================================================
 */
static rxState Rs485Decode(RxPacket *rxPacket, CommProt *commProt, char rxChar) {
    switch (commProt->cRS485State) {
        case WAIT_START:
            rxPacket->cStart = rxChar;
            if (rxChar == PKT_START) /* Check for the start of packet byte */ {
                commProt->cRS485State++;
            }
            break;

        case WAIT_ADDR_HIGH:
            rxPacket->cNetAddr = ((WORD) rxChar) << 8;
            commProt->cRS485State++;
            break;

        case WAIT_ADDR_LOW:
            rxPacket->cNetAddr = (((WORD) rxChar) | rxPacket->cNetAddr);
            commProt->cRS485State++;
            break;

        case WAIT_LEN:
            rxPacket->cLenExpected = rxChar;
            if (rxPacket->cLenExpected > MAX_DATA_LENGTH) {
                commProt->cRS485State = INVALID;
                commProt->cError = BAD_LENGTH;
            } else {
                commProt->cBufPtr = 0;
                commProt->cRS485State = WAIT_DATA;
            }
            break;

        case CMD:
            rxPacket->cCommand = rxChar;
            if (PacketHasPayload()) {
                Nop();
                commProt->cRS485State = WAIT_LEN;
            } else
                commProt->cRS485State = WAIT_CRC_HIGH;
            break;

        case WAIT_DATA:
            rxPacket->c485Buf[(int) commProt->cBufPtr] = rxChar;
            commProt->cBufPtr++;
            if (commProt->cBufPtr == rxPacket->cLenExpected) /* If last byte of data received */ {
                commProt->cRS485State++; /* next byet is the CRC high byte */
            }
            break;

        case WAIT_CRC_HIGH:
            rxPacket->cRxCrc = ((WORD) rxChar << 8);
            commProt->cRS485State++;
            break;

        case WAIT_CRC_LOW:

            /* FIXME */
            rxPacket->cRxCrc = (((WORD) rxChar) | rxPacket->cRxCrc);
            commProt->cRS485State = COMPLETE;
            break;

        case COMPLETE:
            break; /* Idle state */

        case VALID:
            break; /* Idle state */

        case INVALID:
            break; /* Idle state */

        default:
            commProt->cRS485State = WAIT_START;
            break;
    }

    return commProt->cRS485State;
}

/**
 =======================================================================================================================
 * void Rs485BuildPacket( char cAddr, char cCmd, char cLen, char *cData ) ;
 * * Send a packet over the RS485 link ;
 * * Input: TxPacket packet arleady completed without crc ;
 * ;
 * void Rs485SendPacket(char cCmd, char cLen, char *cData, TxCommPack *rxPacket) {
 =======================================================================================================================
 */
int Rs485BuildPacket(unsigned char *str, TxPacket *txPacket) {
    /*~~~~~~~~~~~~~*/
    short int i, j;
    WORD crc;
    /*~~~~~~~~~~~~~*/

    j = 0;

    /* FIXME CRC */
    txPacket->cCalcCrc = 0xffff; /* Clear CRC */

    /*
     * Send some NULL preamblesfopr receiving UART ;
     * for (c = 0;
     * c < NUM_TX_PREAMBLE;
     * c++) Rs485SendChar(0x00);
     * ;
     * FIXME E' sbagliato
     */
    str[j++] = txPacket->cStart;
    //FIXME non provato
    str[j++] = (char)(txPacket->cNetAddr>>8);
    str[j++] = (char)(txPacket->cNetAddr);
    str[j++] = txPacket->cCommand;
    str[j++] = txPacket->cLenExpected;
    for (i = 0; i < txPacket->cLenExpected; i++) {
        str[j++] = txPacket->c485Buf[i];
    }

    str[j] = (char) "\0";

    /*
     * i = strlen(str);
     */
    crc = SW_CRC_Check((BYTE *) str, i, 0xffff);
    txPacket->cCalcCrc = crc;
    str[j++] = ((BYTE) crc) >> 8;
    str[j++] = ((BYTE) crc);
    return j;
}

/**
 =======================================================================================================================
 * void Rs485GetPacket( char *cCommand, char cLen, char *cData ) ;
 * * Pass packet to main application ;
 *
 =======================================================================================================================
 */
void Rs485GetPacket(char *cCom, char *cLen, char *cData, RxPacket *rxPacket) {
    /*~~~~~~*/
    char c;
    /*~~~~~~*/

    *cCom = rxPacket->cCommand;
    *cLen = rxPacket->cLenExpected;
    for (c = 0; c < rxPacket->cLenExpected; c++)
        cData[(int) c] = rxPacket->c485Buf[(int) c];
    cData[(int) rxPacket->cLenExpected] = 0x00; /* Termninate */
}

/**
 =======================================================================================================================
 * char PostValidatePacket(void) ;
 * * Verify the CRC on the last packet received ;
 * * Check if the CRC is correct ;
 * and return the updated state as the result ;
 *
 =======================================================================================================================
 */
char PostValidatePacket(RxPacket *rxPacket) {
    /*~~~~~~~~~~*/
    WORD tmp;
    WORD tmp1;
    char *tmp2;
    /*~~~~~~~~~~*/

    tmp2 = (char *) rxPacket;
    tmp1 = offsetof(struct RxPacket, c485Buf);

    /*
     * CRC16_Init();
     */
    tmp = SW_CRC_Check((const BYTE *) "123456789", 9, 0);
    tmp = SW_CRC_Check((const BYTE *) rxPacket, (WORD) offsetof(struct RxPacket, c485Buf), 0);

    /*
     * Rs485UpdateCrc(PKT_START);
     * ;
     * Rs485UpdateCrc(cNetAddrHigh);
     * Rs485UpdateCrc(cNetAddrLow);
     * Rs485UpdateCrc(cLenExpected);
     * Rs485UpdateCrc(cCommand);
     * if ( PacketHasPayload() ) // If the packet has a payload, { // then include the
     * data in the CRC. for ( c = 0;
     * c < cLenExpected;
     * c++ ) { d = c485Buf[c];
     * Rs485UpdateCrc( d );
     * } } // Check if the CRC is correct // and return the updated state as the result
     * if ( (cRxCrcHigh == cCalcCrcHigh)&&(cRxCrcLow == cCalcCrcLow) ) { cRS485State = PKT_VALID;
     * } else { cError = BAD_CRC;
     * cRS485State = PKT_INVALID;
     * ClearLine2();
     * BinToHexAscii(cRxCrcHigh );
     * BinToHexAscii(cRxCrcLow );
     * LcdWrite(' ');
     * BinToHexAscii(cCalcCrcHigh);
     * BinToHexAscii(cCalcCrcHigh);
     * delay_ms(255);
     * delay_ms(255);
     * delay_ms(255);
     * delay_ms(255);
     * } return cRS485State;
     */
    return VALID;
}

/**
 =======================================================================================================================
 * char GetPacketCmdType(void) ;
 * * Check packet command type ;
 * Return TRUE if packet has a data payload. ;
 *
 =======================================================================================================================
 */
BOOL PacketHasPayload(void) {

    /*
     * if ( cCommand == SENSOR_GET_DATA ) return TRUE;
     * else return FALSE;
     */
    return TRUE;
}

/**
 =======================================================================================================================
 * void BinToHexAscii( char c ) ;
 * * Contributed by: Nick De Smith ;
 *
 =======================================================================================================================
 */
void BinToHexAscii(char c) {

    /*
     * const char hexMap[17] = "0123456789ABCDEF";
     * ;
     * LcdWrite( hexMap[(c >> 4) & 0xF] );
     * LcdWrite( hexMap[(c & 0xF)] );
     */
}
